#ifndef _distance_2_impl_hpp_
#define _distance_2_impl_hpp_

/** \file distance_2_impl.hpp Implements the generic \a distance_2 function induced by the 2-norm
 *  \author Adrian Schweizer
 *  \date   8.7.2007
 */

#include "../math_traits.hpp"

#include <functional>
#include <numeric>
#include <cmath>

#include "../inner_product.hpp"
#include "../aux_functional.hpp"

namespace core {

    namespace math {

        namespace detail {

            template<class V>
                struct distance_2_vector_static_impl
                {
                    typedef typename field<V>::type scalar_type;
                    static scalar_type eval(const V& v1, const V& v0)
                    {
                        using std::sqrt;
                        return sqrt(inner_product(v1,v0,scalar_type(0),aux::plus_sqr<scalar_type>(),std::minus<scalar_type>()));
                    }
                };

            template<class V>
                struct distance_2_vector_dynamic_impl
                {
                    typedef typename field<V>::type scalar_type;
                    static scalar_type eval(const V& v1, const V& v0)
                    {
                        std::size_t dim =   dimension<V>::get(v0);
                        //check that both vectors have the same dimension
                        assert(dim == dimension<V>::get(v1));
                        /*scalar dsqr(0);
                        typedef typename V::const_iterator  const_iterator;
                        const_iterator  iter0=v0.begin(),
                                        iter1=v1.begin();
                        for(const_iterator end0=v0.end();iter0!=end0;++iter0,++iter1)
                        {
                            scalar d = *iter1 - *iter0;
                            dsqr += d*d;
                        }

                        return sqrt(dsqr);*/
                        using std::sqrt;
                        using std::inner_product;
                        return sqrt(inner_product(v1.begin(),v1.end(),v0.begin(),scalar_type(0),aux::plus_sqr<scalar_type>(),std::minus<scalar_type>()));
                    }
                };


            //distance for a vector type
            /** \internal */
            template<class V>
                struct distance_2_vector_impl
                : public boost::mpl::eval_if_c<(dimension<V>::value>0),
                                        distance_2_vector_static_impl<V>,
                                        distance_2_vector_dynamic_impl<V> >
                { };
            /** \internal */
            template<class S>
                struct distance_2_scalar_impl
                {
                    static S eval(const S& s1, const S& s0)
                    {
                        using std::abs;
                        return abs(s1-s0);
                    }
                };
            /** \internal */
            template<class V>
                struct choose_distance_2_impl
                : public boost::mpl::if_<is_scalar<V>,
                                        distance_2_scalar_impl<V>,
                                        distance_2_vector_impl<V> >
                { };

        } // namespace detail

    } // namespace math

} // namespace core

#endif // _distance_impl_hpp_
